package seatiger.tomcat.catalina.loader;

import seatiger.tomcat.catalina.*;
import seatiger.tomcat.catalina.util.LifecycleSupport;
import seatiger.tomcat.naming.resources.FileDirContext;

import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.List;

/**
 * Loader的接口实现
 */
public class WebappLoader implements Loader, Lifecycle,Runnable {

    private static final String info =
            "seatiger.tomcat.catalina.loader.WebappLoader/1.0";

    /**
     * 默认的类加载器
     */
    private String loaderClass =
            "seatiger.tomcat.catalina.loader.WebappClassLoader";

    private Container container;

    private ClassLoader parentClassLoader;

    private LifecycleSupport lifecycle = new LifecycleSupport(this);

    private volatile boolean started = false;

    /**
     * 检查servlet.class是否修改过的后台线程
     */
    private Thread thread = null;

    /**
     * 类加载器
     */
    private WebappClassLoader classLoader;

    /**
     * 多久检查一次文件是否被修改
     */
    private int checkInterval = 15;

    /**
     * 这个后台线程完成的信号量
     */
    private boolean threadDone = false;

    public WebappLoader(){
        this(null);
    }

    public WebappLoader(ClassLoader parent){
        this.parentClassLoader = parent;
    }

    @Override
    public ClassLoader getClassLoader() {
        return classLoader;
    }

    @Override
    public Container getContainer() {
        return container;
    }

    @Override
    public void setContainer(Container container) {
        this.container = container;
    }

    @Override
    public String getInfo() {
        return info;
    }

    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycle.addLifecycleListener(listener);
    }

    @Override
    public List<LifecycleListener> findLifecycleListeners() {
        return lifecycle.findLifecycleListeners();
    }

    @Override
    public void removeLifecycleListener(LifecycleListener listener) {
        lifecycle.removeLifecycleListener(listener);
    }

    @Override
    public void start() throws LifecycleException {
        if (started){
            throw new LifecycleException("webappLoader.alreadyStarted");
        }
        lifecycle.fireLifecycleEvent(START_EVENT,null);
        started = true;
        try {
            //创建类加载器
            classLoader = createClassLoader();
            classLoader.setResources(container.getResources());
            setRepositories();
            startComponents(classLoader);
            //启动线程 检查加载类中 是否时间修改了
            threadStart();
        }catch (Throwable t){
            throw new LifecycleException("start: ", t);
        }
    }

    private void threadStart() {
        if (thread != null){
            return;
        }
        thread = new Thread(this);
        thread.setDaemon(true);
        threadDone = false;
        thread.start();
    }

    private WebappClassLoader createClassLoader() throws Exception{
        Class<?> clazz = Class.forName(loaderClass);
        WebappClassLoader classLoader;
        //反射创建对象
        if (parentClassLoader == null){
            classLoader = (WebappClassLoader) clazz.newInstance();
        }else {
            Class<?>[] argTags = {ClassLoader.class};
            Constructor<?> constructor = clazz.getConstructor(argTags);
            classLoader = (WebappClassLoader) constructor.newInstance(parentClassLoader);
        }
        return classLoader;
    }

    /**
     * 设置仓库
     */
    private void setRepositories() {
        if (!(container instanceof Context)){
            return;
        }

        DirContext resources = container.getResources();
        DirContext classes = null;
        try {
            //这是一个目录
            Object object = resources.lookup(CLASS_PATH);
            if (object instanceof DirContext) {
                classes = (DirContext) object;
            }
        }catch (NamingException e){

        }
        if (classes != null){

            FileDirContext fileDirContext = (FileDirContext) classes;
            File classRepository = new File(fileDirContext.getDocBase());
            classLoader.addRepository(CLASS_PATH + File.separator,classRepository);
        }
    }

    @Override
    public void stop() throws LifecycleException {
        if (!started){
            throw new LifecycleException("webappLoader.notStarted");
        }
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
        threadStop();
        stopComponents(classLoader);
        classLoader = null;
    }

    @Override
    public void run() {
        while (!threadDone){
            threadSleep();
            if (!started){
                break;
            }
            try {
                if (!classLoader.modified()){
                    continue;
                }
            }catch (Exception e){
                continue;
            }
            notifyContext();
            break;
        }
    }

    private void notifyContext() {
        new Thread(() -> ((Context) container).reload()).start();
    }

    private void threadSleep() {
        try {
            Thread.sleep(checkInterval * 1000L);
        } catch (InterruptedException e) {
            ;
        }
    }

    private void threadStop(){
        threadDone = true;
        thread.interrupt();
        try {
            thread.join();
        }catch (InterruptedException e) {
        }
        thread = null;
    }



}
